/*
 * Package il.ac.biu.cs.grossmm.test.FooProtocol
 * File PresenceDoc.java
 * 
 * This is a part of presence service framework API. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.foo.format;

import java.util.HashSet;

public class PresenceDoc {
	public static final String CONTENT_TYPE = "foo.PresenceDoc";
	
	String entity="unknown";
	
	Mood mood;

	Activities activities;

	String location;

	String sphere;

	Tuple[] tuples;

	private static final char DELIM = '|';


	public PresenceDoc(CharSequence serialized)
	{
		int d;
		
		
		if( serialized.charAt(0) != DELIM ) {
			mood =
				new Mood(serialized.charAt(0) - '0', serialized.charAt(1) - '0',
					serialized.charAt(2) - '0', serialized.charAt(3) - '0');
			// skip mood numbers
			d = 4;
		} else
			// skip delimiter
			d = 1;

		int j = 0;

		// find next delimiter
		while (serialized.charAt(d + j) != DELIM)
			j++;

		int pos = j + d+1;

		if (j == 1)
			activities =
				new Activities(Activity.values()[serialized.charAt(d) - '0']);
		else if( j > 0 ){
			Activity[] a = new Activity[j];
			while (j-- > 0)
				a[j] = Activity.values()[serialized.charAt(d + j) - '0'];
			activities = new Activities(a);
		} // else if j == 0 activities = null 
		
		
		j = pos;
		j = span(serialized, j);
		
		entity = serialized.subSequence(pos, j).toString();
		
		pos = ++j;
		j = span(serialized, j);

		if( pos != j )
			location = serialized.subSequence(pos, j).toString();

		pos = ++j;
		j = span(serialized, j);

		if( pos != j )
			sphere = serialized.subSequence(pos, j).toString();

		j++;
		while (serialized.charAt(j) != DELIM)
			j = Tuple.deserialize(this, serialized, j);
	}
	
	public String serialize() {
		return serialize(null).toString();
	}
	
	public StringBuffer serialize(StringBuffer sb)
	{
		if (sb == null)
			sb = new StringBuffer((1 + tuples.length) * 50);
		

		if( mood != null ) {
			sb.append((char)('0' + mood.happiness));
			sb.append((char)('0' + mood.calmness));
			sb.append((char)('0' + mood.weariness));
			sb.append((char)('0' + mood.loneliness));
		} else
			sb.append(DELIM); // tag the condition mood == null

		if( activities != null )
			for (Activity a : activities) {
				sb.append((char)('0' + a.ordinal()));
			}

		sb.append(DELIM);
		
		sb.append(entity).append(DELIM);

		if( location != null )
			sb.append(location);
		
		sb.append(DELIM);
		
		if( sphere != null )
			sb.append(sphere);
		
		sb.append(DELIM);

		if( tuples != null )
			for (Tuple t : tuples) {
				t.serialize(sb);
			}

		sb.append(DELIM);

		return sb;
	}

	/**
	 * @param serialized
	 * @param j
	 * @return
	 */
	private static int span(CharSequence serialized, int j)
	{
		while (serialized.charAt(j) != DELIM)
			j++;
		return j;
	}

	public static class Tuple {
		String id;

		float priority;

		OnlineStatus status;

		String address;

		String tupleClass;

		Capabilities capabilities;

		public Tuple()
		{
		}

		StringBuffer serialize(StringBuffer sb)
		{
			sb.append(id).append(DELIM);
			int n = (int) (priority * 1000);

			for (int i = 0; i < 4; i++) {
				sb.append((char)('0' + (n % 10)));
				n /= 10;
			}

			sb.append((char)('0' + status.ordinal()));

			sb.append(address).append(DELIM);
			
			if( tupleClass != null )
				sb.append(tupleClass);
			
			sb.append(DELIM);

			if( capabilities != null )
				for (String c : capabilities) {
					sb.append(c).append(DELIM);
				}
			
			sb.append(DELIM);

			return sb;
		}

		static int deserialize(PresenceDoc document, CharSequence serialized,
			int j)
		{
			Tuple tuple = new Tuple();

			int pos = j;
			j = span(serialized, j);

			tuple.id = serialized.subSequence(pos, j).toString();
			j++;

			int n = 0;

			int d = 1;
			for (int i = 0; i < 4; i++) {
				n += d*(serialized.charAt(j++) - '0');
				d*=10;
			}

			tuple.priority = n / 1000f;

			char statusChar = serialized.charAt(j++);
			
			tuple.status = OnlineStatus.values()[statusChar - '0'];

			j = span(serialized, pos = j);
			tuple.address = serialized.subSequence(pos, j).toString();

			j = span(serialized, pos = ++j);
			if( pos != j )
				tuple.tupleClass = serialized.subSequence(pos, j).toString();

			if( serialized.charAt(j + 1) != DELIM ) {
				HashSet<String> hs = new HashSet<String>();
	
				while (serialized.charAt(j + 1) != DELIM) {
					j = span(serialized, pos = j+1);
					hs.add(serialized.subSequence(pos, j).toString());
				}
	
				tuple.capabilities = Capabilities.of(hs);
			}

			document.addTupple(tuple);

			return j+2;
		}

		public String getAddress()
		{
			return address;
		}

		public void setAddress(String address)
		{
			this.address = address;
		}

		public Capabilities getCapabilities()
		{
			return capabilities;
		}

		public void setCapabilities(Capabilities capabilities)
		{
			this.capabilities = capabilities;
		}

		public String getId()
		{
			return id;
		}

		public void setId(String id)
		{
			this.id = id;
		}

		public float getPriority()
		{
			return priority;
		}

		public void setPriority(float priority)
		{
			this.priority = priority;
		}

		public OnlineStatus getStatus()
		{
			return status;
		}

		public void setStatus(OnlineStatus status)
		{
			this.status = status;
		}

		public String getTupleClass()
		{
			return tupleClass;
		}

		public void setTupleClass(String tupleClass)
		{
			this.tupleClass = tupleClass;
		}

		/**
		 * @param id
		 * @param priority
		 * @param address
		 * @param status
		 * @param tupleClass
		 * @param capabilities
		 */
		public Tuple(String id, float priority, String address,
			OnlineStatus status, String tupleClass, Capabilities capabilities)
		{
			super();
			this.id = id;
			this.priority = priority;
			this.address = address;
			this.status = status;
			this.tupleClass = tupleClass;
			this.capabilities = capabilities;
		}
	}

	/**
	 * @param mood
	 * @param activities
	 * @param location
	 * @param sphere
	 * @param tuples
	 */
	public PresenceDoc(String entity, Mood mood, Activities activities, String location,
		String sphere, Tuple... tuples)
	{
		this.entity = entity;
		this.mood = mood;
		this.activities = activities;
		this.location = location;
		this.sphere = sphere;
		this.tuples = tuples;
	}

	public PresenceDoc()
	{
	}
	
	public String getEntity()
	{
		return entity;
	}

	public void setEntity(String entity)
	{
		this.entity = entity;
	}

	public Activities getActivities()
	{
		return activities;
	}

	public void setActivities(Activities activities)
	{
		this.activities = activities;
	}

	public String getLocation()
	{
		return location;
	}

	public void setLocation(String location)
	{
		this.location = location;
	}

	public Mood getMood()
	{
		return mood;
	}

	public void setMood(Mood mood)
	{
		this.mood = mood;
	}

	public String getSphere()
	{
		return sphere;
	}

	public void setSphere(String sphere)
	{
		this.sphere = sphere;
	}

	public Tuple[] getTuples()
	{
		return tuples;
	}
	
	public Tuple getTuple(String id)
	{
		int len = tuples.length;
		
		for (int i = 0; i < len; i++)
			if (tuples[i].id.equals(id)) 
				return tuples[i];
		
		return null;
	}

	public void setTuples(Tuple[] tuples)
	{
		this.tuples = tuples;
	}

	public void removeTuple(String id)
	{
		int len = tuples.length;
		
		for (int i = 0; i < len; i++)
			if (tuples[i].id.equals(id)) {
				Tuple[] t = new Tuple[len - 1];
				System.arraycopy(tuples, 0, t, 0, i);
				if( i+1<len )
					System.arraycopy(tuples, i + 1, t, i + 1, len - i - 1);
				
				tuples = t;
			}
	}

	public void addTupple(Tuple tuple)
	{
		if( tuples == null ) {
			tuples = new Tuple[]{tuple};
			return;
		}
			
		
		for (int i = 0; i < tuples.length; i++)
			if (tuples[i].id.equals(tuple.id)) {
				tuples[i] = tuple;
				return;
			}

		Tuple[] t = new Tuple[tuples.length + 1];
		System.arraycopy(tuples, 0, t, 0, tuples.length);
		t[tuples.length] = tuple;
		tuples = t;
	}
	
	
	@Override
	public String toString()
	{
		StringBuffer sb = new StringBuffer('\n');
		sb.append("  Entity = ").append(entity).append('\n');
		
		if( mood != null )
			sb.append("  Mood = ").append(mood).append('\n');
		
		if( location != null )
			sb.append("  Location = ").append(location).append('\n');
		
		if( sphere != null )
			sb.append("  Sphere = ").append(sphere).append('\n');
		
		boolean first = true;
		
		if( activities != null )
			for( Activity a : activities ){
				if( first ) {
					first = false;
					sb.append("  Activities = ");
				} else
					sb.append(", ");
				sb.append(a.toString());
			}
		
		if( ! first )
			sb.append('\n');
		
		if( tuples != null)
			for( Tuple tuple : tuples ) {
				sb.append("  Tuple "+tuple.id);
				
				sb.append("\n     priority     = ").append(tuple.priority);
				sb.append("\n     address      = ").append(tuple.address);
				sb.append("\n     status       = ").append(tuple.status);
				if( tuple.tupleClass!=null )
					sb.append("\n     class        = ").append(tuple.tupleClass);
				
				first = true;
				if( tuple.capabilities != null )
					for( String a : tuple.capabilities){
						if( first ) {
							first = false;
							sb.append("\n     Capabilities = ");
						} else
							sb.append(", ");
						sb.append(a.toString());
					}
				
				if( ! first )
					sb.append('\n');
				
			}
		
		return sb.toString();
	}

	public static void main(String[] args)
	{
	 
		Tuple t = new Tuple(
			"123",
			.66f, 
			"me@kitchen.home.com", 
			OnlineStatus.ONLINE, 
			"personal", 
			Capabilities.of("voice", "video")
			);
		
		Tuple t2 = new Tuple(
			"124",
			.1f, 
			"me@kitchen.home.com", 
			OnlineStatus.OFFLINE, 
			"personal", 
			Capabilities.of("voice")
			);
	
		Tuple t3 = new Tuple(
			"129",
			.8f, 
			"me@kitchen.home.com", 
			OnlineStatus.ONLINE, 
			"personal", 
			Capabilities.of("video")
			);
	
//		PresenceDoc foo  = new PresenceDoc(
//				new Mood(1,8,3,6), 
//				new Activities(Activity.MEAL, Activity.HOLIDAY),
//				"kitchen", 
//				"home", 
//				t, t2, t3
//				);
		
		PresenceDoc foo  = new PresenceDoc(
			"xyz",
			null, 
			null,
			null, 
			null, 
			t, t2, t3
			);
		
		System.out.println(foo);
			
		String str = foo.serialize(null).toString();
		
		System.out.println(str);
		
		System.out.println(str.length());
		
		PresenceDoc foo2 = new PresenceDoc(str);
		System.out.println(foo2);
		
		for( int i=0; i<500000; i++ ) {
			foo = new PresenceDoc(foo.serialize(null));
		}

		long t0 = System.currentTimeMillis();
		
		for( int i=0; i<1000000; i++ ) {
			foo = new PresenceDoc(foo.serialize(null));
		}
		
		long t1 = System.currentTimeMillis();
		
		System.out.println("foo");
		
		System.out.println(t1-t0);
	}
}
